# -*- coding: utf-8 -*-
""" 活动相关的db操作，基本一致…
"""
import logging
from datetime import datetime

from sqlalchemy.exc import IntegrityError
from common import orm, slave
from common.lottery import LOTTERY_TYPE
from common.lottery.cyclical import ACTIVITY_STATUS, NUMBER_COUNT, START_NUM
from common.lottery.cyclical.model import (
    ACTIVITY_MODEL, TREND_MODEL, ORDER_LOGIC)
from common.utils.decorator import sql_wrapper
from common.utils.tz import now_ts, utc_to_local
from common.lottery.cyclical.ssc.order import ssc_get_base_reward_decorator

_WORKER = logging.getLogger('worker')


def fullfill_result(activity_type, term, number, reference):
    # sometimes crawler empty number
    if not number:
        return None
    table = ACTIVITY_MODEL[activity_type]
    inst = table.query.with_for_update().filter(
        table.term == term).first()
    if not inst:
        # 正常情况下不应该走到这里，除非服务器故障缺期
        inst = table()
        inst.term = term
        inst.number = number
        inst.reference = reference
        inst.status = ACTIVITY_STATUS.ANNOUNCED
        inst.end_ts = now_ts()
        inst.announce_ts = inst.end_ts
        inst.save()
        return inst
    if inst.status <= ACTIVITY_STATUS.STOPPED:
        inst.status = ACTIVITY_STATUS.ANNOUNCED
        inst.number = number
        inst.announce_ts = now_ts()
        if not inst.end_ts:
            inst.end_ts = inst.announce_ts
        inst.reference = reference
        inst.save()
        return inst
    else:
        if inst.number != number:
            if activity_type in [LOTTERY_TYPE.GX_KS, LOTTERY_TYPE.JS_KS, LOTTERY_TYPE.FF_KS]:
                if sorted(inst.number) != sorted(number):
                    _WORKER.fatal(u'%s, %s number changed %s -> %s' % (
                        LOTTERY_TYPE.get_label(activity_type), term, inst.number, number))
            else:
                _WORKER.fatal(u'%s, %s number changed %s -> %s' % (
                    LOTTERY_TYPE.get_label(activity_type), term, inst.number, number))
    return None


def get_latest_term(activity_type):
    table = ACTIVITY_MODEL[activity_type]
    inst = orm.session.query(table).order_by(table.term.desc()).first()
    return inst


@sql_wrapper
def get_latest_unpublished_term(activity_type):
    table = ACTIVITY_MODEL[activity_type]
    inst = orm.session.query(table).filter(table.status == ACTIVITY_STATUS.STOPPED).order_by(table.term.desc()).first()
    return inst


def get_latest_unpublished_date(activity_type):
    term = get_latest_unpublished_term(activity_type)
    if not term:
        return None
    start_date = utc_to_local(datetime.fromtimestamp(term.start_ts))
    if len(term.term) < 8:
        return start_date
    try:
        term_date = datetime.strptime(term.term[:8], '%Y%m%d')
        return term_date
    except Exception:
        return start_date


def get_activity_history(activity_type, last_n, status, limit, offset, sort=None):
    table = ACTIVITY_MODEL[activity_type]
    query = slave.session.query(table)
    if status is not None:
        query = query.filter(table.status.in_(status))
    if sort == '-1':
        query = query.order_by(table.term.asc())
    else:
        query = query.order_by(table.term.desc())
    if last_n:
        query = query.limit(last_n)
    else:
        if limit:
            query = query.limit(limit)
        if offset:
            query = query.offset(offset)

    return query.all()


def get_activity_by_term(activity_type, term):
    table = ACTIVITY_MODEL[activity_type]
    return table.query.filter(table.term == term).first()


def stop_term(activity_type, term):
    table = ACTIVITY_MODEL[activity_type]
    inst = table.query.with_for_update().filter(table.term == term).first()
    if inst and inst.status == ACTIVITY_STATUS.STARTED:
        inst.status = ACTIVITY_STATUS.STOPPED
        inst.save()


@ssc_get_base_reward_decorator
def get_base_reward(activity_type, bet_type):
    config = ORDER_LOGIC[activity_type].RATE_CONFIG
    return config.get(bet_type, 0)


def create_new_term(activity_type, term, start_ts, end_ts, missed=False):
    """ term is calced by ts from external
    """
    table = ACTIVITY_MODEL[activity_type]
    inst = table()
    inst.status = ACTIVITY_STATUS.STARTED if not missed else ACTIVITY_STATUS.STOPPED
    inst.term = term
    inst.start_ts = start_ts
    inst.end_ts = end_ts
    try:
        inst.save()
        return True
    except IntegrityError:
        return False


def get_stats_by_term(activity_type, term):
    table = TREND_MODEL[activity_type]
    return table.query.filter(table.term == term).first()


def generate_trend(activity_type, term, number, last_stats):
    table = TREND_MODEL[activity_type]
    trend = table()
    trend.term = term
    trend.number = number
    save = True
    if not number:
        return None
    if ',' in number:
        number = number.split(',')
    for i, n in enumerate(number):
        idx = 'idx_%s' % i
        last = getattr(last_stats, idx).split(',')
        data = ['x'] * len(last)
        for j, stat in enumerate(last):
            data[j] = str(int(stat) + 1) if stat != 'x' else 'x'
        data[int(n) - START_NUM[activity_type]] = '0'
        setattr(trend, idx, ','.join(data))
        if 'x' in data:
            save = False

    return trend, save


def get_activity_stats(activity_type, last_n):
    table = TREND_MODEL[activity_type]
    query = table.query.order_by(table.term.desc()).limit(last_n)
    items = query.all()
    return items


def calc_trend_stats(activity_type, datas):
    '''计算五星单选的趋势统计数据
        `datas`是一个二维数组，内部是（某一位）从0~9的遗漏次数(整数)，
        返回的是该位的统计数据
    '''
    count = NUMBER_COUNT[activity_type]
    result = {
        'max_seq': [0] * count,  # 最大连出
        'max_miss': [0] * count,  # 最大遗漏
        'avg_miss': [0] * count,  # 平均遗漏 = 统计期数 / (命中次数 + 1)
        'hit': [0] * count  # 命中次数
    }
    cur_seq = [0] * count  # 当前连出数据
    for row, row_data in enumerate(datas):
        for col, data in enumerate(row_data):
            if data == 0:
                result['hit'][col] += 1
                cur_seq[col] += 1
            else:
                if cur_seq[col] > result['max_seq'][col]:
                    result['max_seq'][col] = cur_seq[col]
                cur_seq[col] = 0
                if data > result['max_miss'][col]:
                    result['max_miss'][col] = data

    for i, cur in enumerate(cur_seq):
        if cur > result['max_seq'][i]:
            result['max_seq'][i] = cur

    term_count = len(datas)
    for i in xrange(count):
        result['avg_miss'][i] = term_count / (result['hit'][i] + 1)
    return result


def get_stats_by_bet_type(activity_type, bet_type):
    model = TREND_MODEL[activity_type]
    newest = orm.session.query(model).order_by(model.term.desc()).first()
    return newest.get_stat(bet_type) if newest else {}
